package communication;

import java.io.ByteArrayInputStream;

import java.io.IOException;
import java.io.InputStream;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

import communication.message.*;
import communication.message.Error;
import exception.ParseException;

public class MessageXMLParser extends DefaultHandler {
	private String xmlString;
	private Message msg;

	private enum Tag {
		MESSAGE, GAME_OVER, MOVE, GAME_STATE
	}

	Tag tag = null;

	public MessageXMLParser(String str) {
		xmlString = str;
	}

	public Message run() throws ParseException {
		parseDocument();
		return msg;
	}

	private void parseDocument() throws ParseException {
		SAXParserFactory spf = SAXParserFactory.newInstance();
		SAXParser sp;
		try {
			sp = spf.newSAXParser();
			InputStream is = new ByteArrayInputStream(
					xmlString.getBytes("UTF-8"));			
			sp.parse(is, this);
		} catch (SAXException se) {
			throw new ParseException(se.getMessage());
		} catch (IOException e) {
			throw new ParseException(e.getMessage());
		} catch (ParserConfigurationException ignore) {
		}
	}

	// Event Handlers
	public void startElement(String uri, String localName, String qName,
			Attributes attributes) throws SAXException {
		// reset
		try {

			if (tag == null && qName.equalsIgnoreCase("Message")) {
				msg = new Message(
						MessageType.parse(attributes.getValue("type")));
				tag = Tag.MESSAGE;
			}

			if (tag == Tag.MESSAGE) {
				if (qName.equalsIgnoreCase("playerLogin"))
					msg.setPlayerLogin(new PlayerLogin(attributes
							.getValue("nick"), attributes.getValue("gameType")));
				if (qName.equalsIgnoreCase("gameMasterLogin"))
					msg.setGameMasterLogin(new GameMasterLogin(
							attributes.getValue("id"),
							attributes.getValue("gameType"),
							Integer.parseInt(attributes.getValue("playersMin")),
							Integer.parseInt(attributes.getValue("playersMax"))));
				if (qName.equalsIgnoreCase("response")
						&& msg.getType() == MessageType.LOGIN_RESPONSE) {
					msg.setResponse(new Response(attributes.getValue("accept")
							.equalsIgnoreCase("yes")));
				}
				if (qName.equalsIgnoreCase("error"))
					msg.setError(new Error(Integer.parseInt(attributes
							.getValue("id"))));
				if (qName.equalsIgnoreCase("gameId"))
					msg.setGameId(new GameId(attributes.getValue("id")));
				if (qName.equalsIgnoreCase("player"))
					if (msg.getType() == MessageType.CHAMPION_LIST
							|| msg.getType() == MessageType.BEGIN_GAME)
						if (attributes.getValue("won") == null
								&& attributes.getValue("lost") == null)
							msg.addPlayer(new Player(attributes
									.getValue("nick")));
						else {
							msg.addPlayer(new Player(attributes
									.getValue("nick"), Integer
									.parseInt(attributes.getValue("won")),
									Integer.parseInt(attributes
											.getValue("lost"))));
						}
					else
						msg.setPlayer(new Player(attributes.getValue("nick")));
				if (qName.equalsIgnoreCase("nextplayer")) {
					msg.setNextPlayer(new NextPlayer(attributes
							.getValue("nick")));
				}
				if (qName.equalsIgnoreCase("gameOver")) {
					msg.setGameOver(new GameOver());
					tag = Tag.GAME_OVER;
				}
				if (qName.equalsIgnoreCase("gameState")) {
					msg.setGameState(new GameState());
					tag = Tag.GAME_STATE;
				}
				if (qName.equalsIgnoreCase("move")) {
					msg.setMove(new Move());
					tag = Tag.MOVE;
				}
				return;
			}

			if (tag == Tag.GAME_OVER) {
				if (qName.equalsIgnoreCase("player"))
					msg.getGameOver().addPlayer(
							new Player(attributes.getValue("nick"), attributes
									.getValue("result").equalsIgnoreCase(
											"winner") ? Result.WINNER
									: Result.LOOSER));
				return;
			}

			if (tag == Tag.GAME_STATE) {
				if (qName.equalsIgnoreCase("point"))
					msg.getGameState()
							.addPoint(
									new Point(Integer.parseInt(attributes
											.getValue("x")), Integer
											.parseInt(attributes.getValue("y"))));
				return;
			}

			if (tag == Tag.MOVE) {
				if (qName.equalsIgnoreCase("point"))
					msg.getMove()
							.addPoint(
									new Point(Integer.parseInt(attributes
											.getValue("x")), Integer
											.parseInt(attributes.getValue("y"))));
				return;
			}

		} catch (Exception e) {
			throw new ParseException(e.getMessage());
		}
	}

	public void characters(char[] ch, int start, int length)
			throws SAXException {
		if (tag == Tag.MESSAGE && msg.getType() == MessageType.ERROR)
			msg.setError(new String(ch, start, length).trim());
	}

	public void endElement(String uri, String localName, String qName)
			throws SAXException {
		if (qName.equalsIgnoreCase("gameOver")
				|| qName.equalsIgnoreCase("move")
				|| qName.equalsIgnoreCase("gameState"))
			tag = Tag.MESSAGE;
		if (qName.equalsIgnoreCase("message"))
			tag = null;
	}

}
